package service

import (
	"fmt"
	"os/exec"
	"slices"
	"strings"
	"time"

	"gitee.com/actor168/gitserver/pkg/repoutil"
	"github.com/gogs/git-module"
)

type GetCodeBranchModel struct {
	Code       string `json:"code"`
	RepoName   string `json:"repoName"`
	BranchName string `json:"branchName"`
}

type Branch struct {
	RepoPath string
	Name     string

	IsProtected bool
	Commit      *git.Commit
}

// Codebranches implements Service.
func (g *gitRepository) Codebranches(req *GetCodeBranchModel) *GitRespModel {
	repoPath := repoutil.RepoPath(req.Code, req.RepoName)
	gitRepo, err := git.Open(repoPath)
	if err != nil {
		return formatResp(400, fmt.Sprintf("open repository: %v", err), nil)
	}
	names, err := gitRepo.Branches()
	if err != nil {
		return formatResp(500, err.Error(), nil)
	}

	branches, err := g.loadBranches(repoPath, names)
	if err != nil {
		return formatResp(500, err.Error(), nil)
	}
	now := time.Now()
	var DefaultBranch *Branch
	curDefaultBranch, err := getDefaultBranch(repoPath)
	if err != nil {
		return formatResp(500, err.Error(), nil)
	}
	if req.BranchName == "all" {
		activeBranches := make([]*Branch, 0, 3)
		staleBranches := make([]*Branch, 0, 3)

		for i := range branches {
			switch {
			case branches[i].Name == curDefaultBranch:
				DefaultBranch = branches[i]
			case branches[i].Commit.Committer.When.Add(30 * 24 * time.Hour).After(now): // 30 days
				activeBranches = append(activeBranches, branches[i])
			case branches[i].Commit.Committer.When.Add(3 * 30 * 24 * time.Hour).Before(now): // 90 days
				staleBranches = append(staleBranches, branches[i])
			}
		}
		type result struct {
			DefaultBranch  *Branch
			ActiveBranches []*Branch
			StaleBranches  []*Branch
		}
		return formatResp(200, "OK", result{
			DefaultBranch:  DefaultBranch,
			ActiveBranches: activeBranches,
			StaleBranches:  staleBranches,
		})
	} else {
		OtherBranches := make([]*Branch, 0)
		for i := range branches {
			switch {
			case branches[i].Name == curDefaultBranch:
				DefaultBranch = branches[i]
			default:
				OtherBranches = append(OtherBranches, branches[i])
			}
		}
		type result struct {
			DefaultBranch *Branch
			OtherBranches []*Branch
		}
		return formatResp(200, "OK", result{
			DefaultBranch: DefaultBranch,
			OtherBranches: OtherBranches,
		})
	}
}

// 获得精简版分支信息，第一个为默认分支
func (g *gitRepository) getBrefBranchs(code, repoName string) ([]string, error) {
	repoPath := repoutil.RepoPath(code, repoName)
	gitRepo, err := git.Open(repoPath)

	curDefaultBranch, err := getDefaultBranch(repoPath)
	if err != nil {
		return nil, err
	}
	names, err := gitRepo.Branches()
	if err != nil {
		return nil, err
	}

	branches, err := g.loadBranches(repoPath, names)
	if err != nil {
		return nil, err
	}

	res := []string{curDefaultBranch}
	for _, branch := range branches {
		if branch.Name == curDefaultBranch {
			continue
		}
		res = append(res, branch.Name)
	}

	return res, nil
}

func getDefaultBranch(repoPath string) (string, error) {
	// 设置执行命令的工作目录
	cmd := exec.Command("git", "symbolic-ref", "HEAD")
	cmd.Dir = repoPath

	// 执行命令并获取输出结果
	output, err := cmd.Output()
	if err != nil {
		return "", err
	}
	result := strings.Split(string(output), "/")
	branch := result[len(result)-1]
	branch = strings.Replace(branch, "\n", "", -1)
	return branch, nil
}

func (g *gitRepository) loadBranches(repoPath string, rawBranches []string) ([]*Branch, error) {

	protectBranches, err := g.getProtectedBranch(repoPath)
	if err != nil {
		return nil, err
	}

	branches := make([]*Branch, len(rawBranches))
	for i := range rawBranches {
		commit, _ := GetCommit(rawBranches[i], repoPath)

		branches[i] = &Branch{
			Name:   rawBranches[i],
			Commit: commit,
		}

		for j := range protectBranches {
			if branches[i].Name == protectBranches[j] {
				branches[i].IsProtected = true
				break
			}
		}
	}

	return branches, nil
}

type BranchOperateReq struct {
	Code       string `json:"code"`
	RepoName   string `json:"repoName"`
	BranchName string `json:"branchName"`
	Protected  bool   `json:"isProtect"`
}

// SetDefaultBranch implements Service.
func (g *gitRepository) SetDefaultBranch(req *BranchOperateReq) *GitRespModel {
	repoPath := repoutil.RepoPath(req.Code, req.RepoName)
	gitRepo, err := git.Open(repoPath)
	if err != nil {
		return formatResp(400, fmt.Errorf("open repository: %v", err).Error(), nil)
	}
	if !gitRepo.HasBranch(req.BranchName) {
		return formatResp(400, "no branch name found", nil)
	}

	if _, err := gitRepo.SymbolicRef(git.SymbolicRefOptions{
		Ref: git.RefsHeads + req.BranchName,
	}); err != nil {
		return formatResp(500, err.Error(), nil)
	}

	return formatResp(202, "Accepted", nil)
}

// SetProtectedbranches implements Service.
func (g *gitRepository) SetProtectedbranches(req *BranchOperateReq) *GitRespModel {
	repoPath := repoutil.RepoPath(req.Code, req.RepoName)
	if protected_branches, err := g.getProtectedBranch(repoPath); err != nil {
		return formatResp(500, err.Error(), nil)
	} else if !slices.Contains(protected_branches, req.BranchName) {
		err := g.updateProtectedBranch(repoPath, req.BranchName, req.Protected)
		if err != nil {
			return formatResp(500, err.Error(), nil)
		}
	}
	return formatResp(202, "Accepted", nil)
}

type SettingBranchModel struct {
	Code          string `json:"code"`
	RepoName      string `json:"repoName"`
	DefaultBranch string `json:"defaultBranch,omitempty"`
	ProtectBranch string `json:"protectedBranch,omitempty"`
}

func (g *gitRepository) Codesettingbranches(req *SettingBranchModel) *GitRespModel {
	if req.DefaultBranch != "" {
		res := g.SetDefaultBranch(&BranchOperateReq{Code: req.Code, RepoName: req.RepoName, BranchName: req.DefaultBranch})
		if res.Code/100 > 2 {
			return res
		}
	}
	if req.ProtectBranch != "" {
		// 从保护分支中去除当前分支
		res := g.SetProtectedbranches(&BranchOperateReq{Code: req.Code, RepoName: req.RepoName, BranchName: req.DefaultBranch, Protected: true})
		if res.Code/100 > 2 {
			return res
		}
	}

	return formatResp(202, "Accepted", map[string]any{
		"DefaultBranch": req.DefaultBranch,
		"ProtectBranch": req.ProtectBranch,
	})
}

func (g *gitRepository) ListBriefBranchs(req *GetCodeBranchModel) *GitRespModel {
	repoPath := repoutil.RepoPath(req.Code, req.RepoName)
	gitRepo, err := git.Open(repoPath)
	if err != nil {
		return formatResp(400, fmt.Sprintf("open repository: %v", err), nil)
	}
	names, err := gitRepo.Branches()
	if err != nil {
		return formatResp(500, err.Error(), nil)
	}

	branches, err := g.loadBranches(repoPath, names)
	if err != nil {
		return formatResp(500, err.Error(), nil)
	}
	curDefaultBranch, err := getDefaultBranch(repoPath)
	if err != nil {
		return formatResp(500, err.Error(), nil)
	}

	type res struct {
		AllBranches       []string
		DefaultBranch     string
		ProtectedBranches []string
	}
	r := res{
		DefaultBranch:     curDefaultBranch,
		ProtectedBranches: make([]string, 0),
	}
	for i := range branches {
		if branches[i].IsProtected {
			r.ProtectedBranches = append(r.ProtectedBranches, branches[i].Name)
		}
		r.AllBranches = append(r.AllBranches, branches[i].Name)
	}
	return formatResp(200, "OK", r)
}
